package com.kersci.heartratereader

import android.app.Service
import android.bluetooth.BluetoothGatt
import android.content.Intent
import android.os.Handler
import android.os.HandlerThread
import android.os.IBinder
import android.os.Message
import android.util.EventLog
import com.clj.fastble.BleManager
import com.clj.fastble.callback.BleGattCallback
import com.clj.fastble.callback.BleNotifyCallback
import com.clj.fastble.callback.BleScanCallback
import com.clj.fastble.data.BleDevice
import com.clj.fastble.exception.BleException
import com.clj.fastble.scan.BleScanRuleConfig
import com.kersci.heartratereader.utils.DeviceManager
import com.kersci.heartratereader.utils.HeartRateDeviceConnectEvent
import com.safframework.log.L
import com.safframework.log.extension.json
import org.greenrobot.eventbus.EventBus
import java.util.*

class BleDeviceService : Service() {
    companion object {
        const val ACTION_START_SEARCHING = "ACTION_START_SEARCHING"
        const val ACTION_START_CONNECT = "ACTION_START_CONNECT"
        const val ACTION_STOP_OPERATION = "ACTION_STOP_OPERATION"
        const val ACTION_START_NOTIFY = "ACTION_START_NOTIFY"

        const val KEY_DEVICE_POSITION = "KEY_DEVICE_POSITION"

        const val EVENT_START_SEARCHING = 0
        const val EVENT_START_CONNECT_DEVICE = 1
        const val EVENT_STOP_OPERATION = 2
        const val EVENT_START_NOTIFY = 3

        const val UUID_HRM_SERVICE = "0000180d-0000-1000-8000-00805f9b34fb"
        const val UUID_HRM_CHARACTERISTIC = "00002a37-0000-1000-8000-00805f9b34fb"
    }

    private lateinit var deviceManager: DeviceManager
    private lateinit var handlerThread: HandlerThread
    private lateinit var handler : Handler
    private lateinit var bleGattCallback : BleGattCallback
    private lateinit var bleNotifyCallback: BleNotifyCallback

    override fun onCreate() {
        super.onCreate()
        L.d("onCreate")
        deviceManager = App.instance.getDeviceManager()
        handlerThread = HandlerThread(BleDeviceService::class.simpleName + "-Handler")
        handlerThread.start()
        handler = object : Handler(this.mainLooper) {
            override fun handleMessage(msg: Message) {
                super.handleMessage(msg)
                L.d("receive $msg")
                handleBleMessage(msg)
            }
        }

        bleGattCallback = object : BleGattCallback() {
            override fun onStartConnect() {
                L.d("onStartConnect")
            }

            override fun onConnectFail(bleDevice: BleDevice?, exception: BleException?) {
                L.d("onConnectFail => " + bleDevice?.name)
                EventBus.getDefault().post(HeartRateDeviceConnectEvent(HeartRateDeviceConnectEvent.EVENT_CONNECT_FAIL, bleDevice))
            }

            override fun onConnectSuccess(bleDevice: BleDevice?, gatt: BluetoothGatt?, status: Int) {
                L.d("onConnectSuccess =>" + bleDevice?.name)
                EventBus.getDefault().post(HeartRateDeviceConnectEvent(HeartRateDeviceConnectEvent.EVENT_CONNECTED, bleDevice))
            }

            override fun onDisConnected(isActiveDisConnected: Boolean, device: BleDevice?, gatt: BluetoothGatt?, status: Int) {
                L.d("onDisConnected => " + device?.name)
            }
        }

        bleNotifyCallback = object: BleNotifyCallback() {
            override fun onNotifySuccess() {
                L.d("onNotifySuccess")
            }

            override fun onNotifyFailure(exception: BleException?) {
                L.d("onNotifyFailure")
            }

            override fun onCharacteristicChanged(data: ByteArray?) {
                EventBus.getDefault().post(HeartRateDeviceConnectEvent(HeartRateDeviceConnectEvent.EVENT_NOTIFY_DATA_CHANGED, null))
            }
        }
    }
    private fun getDeviceFromMessage(msg: Message): BleDevice ? {
        val position = msg.arg1
        val heartRateDevice = deviceManager.getDevice(position)
        return heartRateDevice?.bleDevice
    }

    private fun handleBleMessage(msg: Message) {
        L.json(msg)
        val bleDevice = getDeviceFromMessage(msg)

        when(msg.what) {
            EVENT_START_SEARCHING -> {
                L.d("START Searching")
                deviceManager.clear()
                startSearchBleDevice()
            }

            EVENT_START_CONNECT_DEVICE -> {
                if (bleDevice != null) {
                    BleManager.getInstance().connect(bleDevice, bleGattCallback)
                }
            }

            EVENT_START_NOTIFY -> {
                getDeviceFromMessage(msg)
                if (bleDevice != null) {
                    L.d("handle EVENT_START_NOTIFY")
                    BleManager.getInstance().notify(bleDevice, UUID_HRM_SERVICE, UUID_HRM_CHARACTERISTIC, bleNotifyCallback)
                }
            }

            EVENT_STOP_OPERATION -> {
                if (bleDevice != null) {
                    BleManager.getInstance().stopNotify(bleDevice, UUID_HRM_SERVICE, UUID_HRM_CHARACTERISTIC)
                    if(BleManager.getInstance().isConnected(bleDevice)) {
                        BleManager.getInstance().disconnect(bleDevice)
                    }
                    BleManager.getInstance().cancelScan()
                }
            }
        }
    }

    private fun startSearchBleDevice() {
        val uuids = arrayListOf<UUID>()
        uuids.add(UUID.fromString(UUID_HRM_SERVICE))

        val scanRuleConfig = BleScanRuleConfig.Builder()
            .setServiceUuids(uuids.toTypedArray())
            .setScanTimeOut(10000)
            .build()
        BleManager.getInstance().initScanRule(scanRuleConfig)

        val callback = object : BleScanCallback() {
            override fun onScanStarted(success: Boolean) {
                L.d("onScanStarted")
                deviceManager.notifyBleScanEventChanged(DeviceManager.EVENT_SCAN_STARTED)
            }

            override fun onScanFinished(scanResultList: MutableList<BleDevice>?) {
                L.d("onScanFinished")
                deviceManager.notifyBleScanEventChanged(DeviceManager.EVENT_SCAN_FINISHED)
            }

            override fun onScanning(bleDevice: BleDevice?) {
                val name = bleDevice?.name
                val mac = bleDevice?.mac
                val rssi = bleDevice?.rssi

                L.d("name - $name, mac - $mac, rssi - $rssi")
                val deviceManager = App.instance.getDeviceManager()
                if (name != null && mac != null && rssi != null) {
                    deviceManager.addDevice(name, mac, rssi, bleDevice)
                }
                deviceManager.notifyBleScanEventChanged(DeviceManager.EVENT_SCANNING)
            }

            override fun onLeScan(bleDevice: BleDevice?) {

            }
        }

        BleManager.getInstance().scan(callback)
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        val action = intent?.action
        L.d(action)
        val position = intent?.getIntExtra(KEY_DEVICE_POSITION, -1) as Int
        when(action) {
            ACTION_START_SEARCHING -> {
                L.d("send message")
                handler.sendEmptyMessage(EVENT_START_SEARCHING)
            }

            ACTION_START_CONNECT -> {
                L.d("connect device")

                if (position != -1) {
                    val msg = handler.obtainMessage(EVENT_START_CONNECT_DEVICE, position, 0)
                    handler.sendMessage(msg)
                }
            }

            ACTION_STOP_OPERATION -> {
                L.d("stop operation")

                if (position != -1) {
                    val msg = handler.obtainMessage(EVENT_STOP_OPERATION, position, 0)
                    handler.sendMessage(msg)
                }
            }

            ACTION_START_NOTIFY -> {
                L.d("start notify")
                if (position != -1) {
                    val msg = handler.obtainMessage(EVENT_START_NOTIFY, position, 0)
                    handler.sendMessage(msg)
                }
            }
        }
        return super.onStartCommand(intent, flags, startId)
    }

    override fun onBind(intent: Intent): IBinder? {
        return null
    }

    override fun onDestroy() {
        super.onDestroy()
        L.d("onDestroy")
    }
}